<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
</body>
</html>
<script>
    // 解构不仅可用于数组 还可以用于对象
    // 对象的解构与数组不同 数组的元素必须是按顺序排列 变量的取值由它的位置决定
    // 对象的属性没有顺序 变量名必须要与属性同名
    let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
    foo // "aaa"
    bar // "bbb"

    let { baz } = { foo: 'aaa', bar: 'bbb' };
    baz // undefined 解构失败变量的值等于undefined

    // 对象的解构赋值 可以方便地将现有对象的方法 赋值到某个变量
    // 例一
    let { log, sin, cos } = Math;

    // 例二                  注意这句话  =======> 将console.log赋值到log变量!!!
    const { log } = console;
    log('hello') // hello

    // 如果变量名与属性名不一致 必须写成下面这样
    let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
    baz // "aaa"

    let obj = { first: 'hello', last: 'world' };
    let { first: f, last: l } = obj;
    f // 'hello'
    l // 'world'

    // 对象的解构赋值的内部机制 先找到同名属性 再赋值给对应的变量 真正被赋值的是后者 不是前者
    let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };

    let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
    baz // "aaa"
    foo // error: foo is not defined

    // 解构也适用于嵌套结构的对象
    let obj = {
        p: [
            'Hello',
            { y: 'World' }
        ]
    };

    let { p: [x, { y }] } = obj; // p是模式不是变量
    x // "Hello"
    y // "World"

    // 如果p也要作为变量赋值 可以写成下面这样
    let { p, p: [x, { y }] } = obj;
    x // "Hello"
    y // "World"
    p // ["Hello", {y: "World"}]

    // 再举一个例子 注意理解
    const node = {
        loc: {
            start: {
                line: 1,
                column: 5
            }
        }
    };

    let { loc, loc: { start }, loc: { start: { line }} } = node;
    line // 1
    loc  // Object {start: Object}
    start // Object {line: 1, column: 5}

    // 再举一个例子
    let obj = {};
    let arr = [];

    ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });

    obj // {prop:123}
    arr // [true]

    // 如果结构模式是嵌套的对象 子对象所在的父属性不存在 将会报错
    // 报错
    let {foo: {bar}} = {baz: 'baz'};

    // 对象的解构可以取到继承的属性
    // 对象obj1的原型对象是obj2。foo属性不是obj1自身的属性，而是继承自obj2的属性，解构赋值可以取到这个属性。
    const obj1 = {};
    const obj2 = { foo: 'bar' };
    Object.setPrototypeOf(obj1, obj2); // ?

    const { foo } = obj1;
    foo // "bar"


    // 默认值
    // 对象的解构可以指定默认值
    var {x = 3} = {};
    x // 3

    var {x, y = 5} = {x: 1};
    x // 1
    y // 5

    var {x: y = 3} = {};
    y // 3

    var {x: y = 3} = {x: 5};
    y // 5

    var { message: msg = 'Something went wrong' } = {};
    msg // "Something went wrong"

    // 默认值的生效条件是 对象的属性严格等于undefined
    var {x = 3} = {x: undefined};
    x // 3

    var {x = 3} = {x: null};
    x // null

</script>